package me.keiwu.user.service.impl;

import me.keiwu.pub.data.ConfigType;
import me.keiwu.pub.data.LockKeyEnum;
import me.keiwu.pub.data.Response;
import me.keiwu.pub.data.config.AppConfigBean;
import me.keiwu.pub.data.config.ChallengeConfigBean;
import me.keiwu.pub.data.wx.WXStepData;
import me.keiwu.pub.data.wx.WXStepInfo;
import me.keiwu.pub.util.CacheUtil;
import me.keiwu.pub.util.DateUtil;
import me.keiwu.pub.util.redis.RedisLock;
import me.keiwu.user.dao.TotalStepMarkDao;
import me.keiwu.user.dao.UserStepDao;
import me.keiwu.user.dao.UserTotalStepDao;
import me.keiwu.user.domain.TotalStepMark;
import me.keiwu.user.domain.User;
import me.keiwu.user.domain.UserStep;
import me.keiwu.user.domain.UserTotalStep;
import me.keiwu.user.rpc.ChallengeRpc;
import me.keiwu.user.service.StepService;
import me.keiwu.user.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Created by kei on 10/11/2017.
 */
@Service
@Transactional
public class StepServiceImpl implements StepService {

    private static final Logger logger = LoggerFactory.getLogger(StepServiceImpl.class);


    @Autowired
    private UserService userService;
    @Autowired
    private UserStepDao userStepDao;
    @Autowired
    private ChallengeRpc challengeRpc;
    @Autowired
    private UserTotalStepDao userTotalStepDao;
    @Autowired
    private TotalStepMarkDao totalStepMarkDao;

    @Autowired
    private CacheUtil cacheUtil;
    @Autowired
    private RedisLock redisLock;



    @Override
    public Response exchangeStep(long userId) {
        User user = userService.getUserById(userId);
        int days = DateUtil.getDayStrInt(new Date());
        UserStep step = userStepDao.findInSameDay(userId, days);
        if (step == null) return Response.fail("还没有提交步数");
        AppConfigBean config = cacheUtil.getConfig(ConfigType.APP_CONFIG, AppConfigBean.class);
        if (config == null) return Response.fail("没有配置数据");

        int maxExchange = config.getMaxExchange() == null ? Integer.MAX_VALUE - 1 : config.getMaxExchange();
        if (maxExchange <= step.getExchangedStep()) {
            return Response.fail("今天已行走超过20000步了，您已达到每天可兑换上限啦，注意休息哦！");
        }

        int exchangeStep = step.getStep() - step.getExchangedStep();
        int maxExchangeStep = maxExchange - step.getExchangedStep();
        boolean isFull = false;
        if (exchangeStep > maxExchangeStep) {
            isFull = true;
            exchangeStep = maxExchangeStep;
        }
        int count = (exchangeStep / config.getExchangeUnit());
        if (count <= 0 && isFull) {
            return Response.fail("今天已行走超过20000步了，您已达到每天可兑换上限啦，注意休息哦！");
        } else if (count <= 0) {
            return Response.fail("您的能量不足，赶紧走起来吧！");
        }
        int exchangeGold = count * config.getExchangeGold();
        if (userService.changeGold(user, exchangeGold)) {
            step.setExchangedStep(step.getExchangedStep() + count * config.getExchangeUnit());
            userStepDao.save(step);
//            JSONObject json = new JSONObject();
//            json.put("exchangeGold", exchangeGold);
//            json.put("isFull", isFull);
//            json.put("maxExchange", maxExchange);
//            return Response.ok(json);
            return Response.ok(exchangeGold);
        }
        return Response.fail("兑换失败");
    }


    @Override
    public List<UserStep> saveStepData(long userId, WXStepData stepData) {
        List<UserStep> userSteps = new ArrayList<>();

        for (WXStepInfo info : stepData.getStepInfoList()) {
            int days = DateUtil.getDayStrInt(new Date(info.getTimestamp() * 1000L));
            UserStep step = userStepDao.findInSameDay(userId, days);
            if (step == null) {
                step = new UserStep();
                step.setUserId(userId);
            }
            step.setStep(info.getStep());
            step.setTimestamp(info.getTimestamp());
            step.setDays(days);
            userSteps.add(step);
        }
        userStepDao.save(userSteps);

        // 统计总步数
        this.updateTotalStep(userId, userSteps);

        // 同步更新任务步数
        challengeRpc.triggerUpdateStep(userId);

        // 清除用户缓存信息
        userService.cleanUserCache(userId);

        return userSteps;
    }


    private void updateTotalStep(long userId, List<UserStep> userSteps) {
        User user = userService.getUserById(userId);
        int todayDays = DateUtil.getDayStrInt(new Date());

        String lockKey = LockKeyEnum.UPDATE_USER_TOTAL_STEP.getLockKey(userId);
        String requestId = UUID.randomUUID().toString();
        try {
            if (!redisLock.tryLock(lockKey, requestId)) {
                logger.warn("updateTotalStep get redis lock fail! lockKey={}, requestId={}", lockKey, requestId);
                return;
            }
            int addTotalSteps = 0;
            List<TotalStepMark> updateMarkList = new ArrayList<>();
            List<Integer> daysList = new ArrayList<>();
            userSteps.forEach(us -> daysList.add(us.getDays()));
            List<Integer> markDaysList = totalStepMarkDao.findAllByUserIdAndDaysList(userId, daysList);
//            logger.info("updateTotalStep userId={}, daysList={}, markDaysList={}", userId, daysList, markDaysList);

            int totalStep30 = 0;
            for (UserStep userStep : userSteps) {
                totalStep30 += userStep.getStep();

                // 从创建用户开始算
                if (DateUtil.getDayStart(user.getCreatedTime()).getTime() / 1000 > userStep.getTimestamp()) continue;
                // 今天的不计算
                if (userStep.getDays() >= todayDays) continue;
                if (markDaysList.contains(userStep.getDays())) continue; // 已经标记了

                addTotalSteps += userStep.getStep();
                // 标记统计了
                TotalStepMark markEntity = new TotalStepMark();
                markEntity.setUserId(userId);
                markEntity.setDays(userStep.getDays());
                updateMarkList.add(markEntity);
            }
            // 更新总步数
            UserTotalStep totalStep = userTotalStepDao.findOne(userId);
            if (totalStep == null) {
                totalStep = new UserTotalStep();
                totalStep.setUserId(userId);
                totalStep.setTotalStep(0);
                totalStep.setTotalStep30(0);
            }
            if (totalStep30 != 0) {
                totalStep.setTotalStep30(totalStep30);
            }
            totalStep.setTotalStep(totalStep.getTotalStep() + addTotalSteps);
            userTotalStepDao.save(totalStep);
            totalStepMarkDao.save(updateMarkList);
        } catch (Exception e) {
            logger.warn("updateTotalStep fail! lockKey={}, requestId={}, exception: ", lockKey, requestId, e);
        } finally {
            redisLock.release(lockKey, requestId);
        }
        int beforeDays = DateUtil.getDayStrInt(DateUtil.addDays(new Date(), -50));
        totalStepMarkDao.clean(userId, beforeDays);
    }

}
